package com.scale.service.user.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.common.utils.JwtUtils;
import com.example.user.dto.ResetPasswordDTO;
import com.example.user.dto.UserLoginDTO;
import com.example.user.dto.UserRegisterDTO;
import com.example.user.entity.Users;
import com.example.user.vo.LoginResultVO;
import com.example.user.vo.UserInfoVO;
import com.scale.service.user.mapper.UsersMapper;
import com.scale.service.user.service.UserAuthService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;

/**
 * 用户认证服务实现类
 * @author crp
 * @since 2025-09-23
 */
@Slf4j
@Service
public class UserAuthServiceImpl implements UserAuthService {

    @Autowired
    private UsersMapper usersMapper;

    @Resource
    private JwtUtils jwtUtils;

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * MD5加密密码
     */
    private String encodePassword(String password) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] digest = md.digest(password.getBytes());
            StringBuilder sb = new StringBuilder();
            for (byte b : digest) {
                sb.append(String.format("%02x", b));
            }
            return sb.toString();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("密码加密失败", e);
        }
    }

    /**
     * 验证密码
     */
    private boolean matchesPassword(String rawPassword, String encodedPassword) {
        return encodePassword(rawPassword).equals(encodedPassword);
    }

    // 邮箱正则
    private final Pattern EMAIL_PATTERN = Pattern.compile("^[A-Za-z0-9+_.-]+@(.+)$");
    // 手机号正则
    private final Pattern PHONE_PATTERN = Pattern.compile("^1[3-9]\\d{9}$");

    @Override
    public void register(UserRegisterDTO registerDTO) {
        // 1. 验证确认密码
        if (!registerDTO.getPassword().equals(registerDTO.getConfirmPassword())) {
            throw new RuntimeException("两次输入的密码不一致");
        }

        // 2. 验证验证码
        String cacheKey = "verify_code:" + registerDTO.getVerifyType() + ":" +
                         ("email".equals(registerDTO.getVerifyType()) ? registerDTO.getEmail() : registerDTO.getPhone());
        String cachedCode = redisTemplate.opsForValue().get(cacheKey);
        if (!registerDTO.getVerifyCode().equals(cachedCode)) {
            throw new RuntimeException("验证码错误或已过期");
        }

        // 3. 检查用户名是否已存在
        LambdaQueryWrapper<Users> usernameQuery = new LambdaQueryWrapper<>();
        usernameQuery.eq(Users::getUsername, registerDTO.getUsername());
        if (usersMapper.selectCount(usernameQuery) > 0) {
            throw new RuntimeException("用户名已存在");
        }

        // 4. 检查邮箱/手机号是否已存在
        LambdaQueryWrapper<Users> accountQuery = new LambdaQueryWrapper<>();
        if ("email".equals(registerDTO.getVerifyType())) {
            accountQuery.eq(Users::getEmail, registerDTO.getEmail());
        } else {
            accountQuery.eq(Users::getPhone, registerDTO.getPhone());
        }
        if (usersMapper.selectCount(accountQuery) > 0) {
            throw new RuntimeException("该邮箱/手机号已被注册");
        }

        // 5. 创建用户
        Users user = new Users();
        user.setUsername(registerDTO.getUsername());
        user.setEmail(registerDTO.getEmail());
        user.setPhone(registerDTO.getPhone());
        user.setPassword(encodePassword(registerDTO.getPassword()));
        user.setStatus(1); // 正常状态
        user.setCreatedAt(LocalDateTime.now());
        user.setUpdatedAt(LocalDateTime.now());

        int result = usersMapper.insert(user);
        if (result <= 0) {
            throw new RuntimeException("注册失败，请重试");
        }

        // 6. 删除验证码
        redisTemplate.delete(cacheKey);

        log.info("用户注册成功，用户名: {}", registerDTO.getUsername());
    }

    @Override
    public LoginResultVO login(UserLoginDTO loginDTO) {
        // 1. 查找用户
        Users user = findUserByAccount(loginDTO.getAccount());
        if (user == null) {
            throw new RuntimeException("用户不存在");
        }

        // 2. 验证密码
        if (!matchesPassword(loginDTO.getPassword(), user.getPassword())) {
            throw new RuntimeException("密码错误");
        }

        // 3. 检查用户状态
        if (user.getStatus() == 0) {
            throw new RuntimeException("账号已被禁用");
        }

        // 4. 生成令牌
        String accessToken = jwtUtils.generateAccessToken(user.getId(), user.getUsername());
        String refreshToken = jwtUtils.generateRefreshToken(user.getId());

        // 5. 更新最后登录时间
        user.setLastLoginTime(LocalDateTime.now());
        usersMapper.updateById(user);

        // 6. 缓存刷新令牌
        redisTemplate.opsForValue().set("refresh_token:" + user.getId(), refreshToken, 7, TimeUnit.DAYS);

        // 7. 构建返回结果
        LoginResultVO result = new LoginResultVO();
        result.setAccessToken(accessToken);
        result.setRefreshToken(refreshToken);
        result.setExpiresIn(86400L); // 24小时
        result.setUserInfo(convertToUserInfoVO(user));

        log.info("用户登录成功，用户ID: {}", user.getId());
        return result;
    }

    @Override
    public void logout(String token) {
        try {
            Long userId = jwtUtils.getUserIdFromToken(token);
            // 删除刷新令牌
            redisTemplate.delete("refresh_token:" + userId);
            // 将访问令牌加入黑名单
            redisTemplate.opsForValue().set("blacklist_token:" + token, "1", 24, TimeUnit.HOURS);
            log.info("用户登出成功，用户ID: {}", userId);
        } catch (Exception e) {
            log.warn("登出时解析令牌失败: {}", e.getMessage());
        }
    }

    @Override
    public LoginResultVO refreshToken(String refreshToken) {
        try {
            // 1. 验证刷新令牌
            if (!jwtUtils.validateToken(refreshToken) || !"refresh".equals(jwtUtils.getTokenType(refreshToken))) {
                throw new RuntimeException("刷新令牌无效");
            }

            Long userId = jwtUtils.getUserIdFromToken(refreshToken);

            // 2. 检查缓存中的刷新令牌
            String cachedRefreshToken = redisTemplate.opsForValue().get("refresh_token:" + userId);
            if (!refreshToken.equals(cachedRefreshToken)) {
                throw new RuntimeException("刷新令牌已失效");
            }

            // 3. 获取用户信息
            Users user = usersMapper.selectById(userId);
            if (user == null || user.getStatus() == 0) {
                throw new RuntimeException("用户不存在或已被禁用");
            }

            // 4. 生成新的令牌
            String newAccessToken = jwtUtils.generateAccessToken(user.getId(), user.getUsername());
            String newRefreshToken = jwtUtils.generateRefreshToken(user.getId());

            // 5. 更新缓存
            redisTemplate.opsForValue().set("refresh_token:" + userId, newRefreshToken, 7, TimeUnit.DAYS);

            // 6. 构建返回结果
            LoginResultVO result = new LoginResultVO();
            result.setAccessToken(newAccessToken);
            result.setRefreshToken(newRefreshToken);
            result.setExpiresIn(86400L);
            result.setUserInfo(convertToUserInfoVO(user));

            return result;
        } catch (Exception e) {
            throw new RuntimeException("刷新令牌失败: " + e.getMessage());
        }
    }

    @Override
    public void sendVerifyCode(String account, String type) {
        // 1. 验证账号格式
        if ("email".equals(type) && !EMAIL_PATTERN.matcher(account).matches()) {
            throw new RuntimeException("邮箱格式不正确");
        }
        if ("phone".equals(type) && !PHONE_PATTERN.matcher(account).matches()) {
            throw new RuntimeException("手机号格式不正确");
        }

        // 2. 生成验证码
        String code = String.valueOf((int) ((Math.random() * 900000) + 100000));

        // 3. 存储验证码（5分钟过期）
        String cacheKey = "verify_code:" + type + ":" + account;
        redisTemplate.opsForValue().set(cacheKey, code, 5, TimeUnit.MINUTES);

        // 4. 发送验证码（这里仅模拟，实际需要集成短信/邮件服务）
        if ("email".equals(type)) {
            // TODO: 集成邮件服务
            log.info("发送邮箱验证码到 {}: {}", account, code);
        } else {
            // TODO: 集成短信服务
            log.info("发送短信验证码到 {}: {}", account, code);
        }
    }

    @Override
    public void resetPassword(ResetPasswordDTO resetPasswordDTO) {
        // 1. 验证确认密码
        if (!resetPasswordDTO.getNewPassword().equals(resetPasswordDTO.getConfirmPassword())) {
            throw new RuntimeException("两次输入的密码不一致");
        }

        // 2. 验证验证码
        String cacheKey = "verify_code:" + resetPasswordDTO.getVerifyType() + ":" + resetPasswordDTO.getAccount();
        String cachedCode = redisTemplate.opsForValue().get(cacheKey);
        if (!resetPasswordDTO.getVerifyCode().equals(cachedCode)) {
            throw new RuntimeException("验证码错误或已过期");
        }

        // 3. 查找用户
        Users user = findUserByAccount(resetPasswordDTO.getAccount());
        if (user == null) {
            throw new RuntimeException("用户不存在");
        }

        // 4. 更新密码
        user.setPassword(encodePassword(resetPasswordDTO.getNewPassword()));
        user.setUpdatedAt(LocalDateTime.now());
        usersMapper.updateById(user);

        // 5. 删除验证码
        redisTemplate.delete(cacheKey);

        // 6. 清除该用户的所有令牌
        redisTemplate.delete("refresh_token:" + user.getId());

        log.info("用户重置密码成功，用户ID: {}", user.getId());
    }

    @Override
    public boolean validateToken(String token) {
        // 检查令牌是否在黑名单中
        if (redisTemplate.hasKey("blacklist_token:" + token)) {
            return false;
        }
        return jwtUtils.validateToken(token);
    }

    @Override
    public Long getUserIdFromToken(String token) {
        return jwtUtils.getUserIdFromToken(token);
    }

    /**
     * 根据账号查找用户（支持用户名、邮箱、手机号）
     */
    private Users findUserByAccount(String account) {
        LambdaQueryWrapper<Users> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Users::getUsername, account)
                   .or().eq(Users::getEmail, account)
                   .or().eq(Users::getPhone, account);
        return usersMapper.selectOne(queryWrapper);
    }

    /**
     * 转换为用户信息VO
     */
    private UserInfoVO convertToUserInfoVO(Users user) {
        UserInfoVO userInfoVO = new UserInfoVO();
        BeanUtils.copyProperties(user, userInfoVO);

        // 手机号脱敏
        if (StringUtils.hasText(user.getPhone())) {
            userInfoVO.setPhone(user.getPhone().replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2"));
        }

        return userInfoVO;
    }
}
